import { LazyList, LazyNumber, printList } from "./types"
import { repeat, map, cons, take } from "./common";
import { within, square, round, log2 } from "./number"
import { improve, superr } from "./compute";



///////////////////////// Derivative [BASIC] /////////////////////////

// easyDiff(f)(x)(h)：f(x) 在 [x, x + h] 的导数
const easyDiff =
    (f: (a: LazyNumber) => LazyNumber) =>
        (x: LazyNumber) =>
            (h: LazyNumber): LazyNumber => {
                const x1 = x();
                const x2 = x1 + h();
                const y2 = f(() => x2)
                const y1 = f(() => x1)
                return () => ((y2() - y1()) / (x2 - x1))
            }

// halve(x) = x / 2
const halve =
    (x: LazyNumber): LazyNumber =>
        () => (x() / 2);

// differentiate(h0)(f)(x)：f(x) 在 [x, x + delta] 的导数，delta 为 [h0, h0/2, h0/4, ...]
const differentiate =
    (h0: LazyNumber) =>
        (f: (a: LazyNumber) => LazyNumber) =>
            (x: LazyNumber): LazyList<number> =>
                () => map<number, number>
                    (easyDiff(f)(x))
                    (repeat(halve)(h0))()

const derivative =
    (h0: LazyNumber) =>
        (eps: LazyNumber) =>
            (f: (a: LazyNumber) => LazyNumber) =>
                (x: LazyNumber) =>
                    within(eps)(() => differentiate(h0)(f)(x)())



///////////////////////// Derivative [BETTER] /////////////////////////

const betterDerivative =
    (h0: LazyNumber) =>
        (eps: LazyNumber) =>
            (f: (a: LazyNumber) => LazyNumber) =>
                (x: LazyNumber): LazyNumber =>
                    within(eps)(
                        improve(
                            improve(
                                improve(
                                    differentiate(h0)(f)(x)
                                )
                            )
                        )
                    )



///////////////////////// Derivative [SUPER] /////////////////////////

const superDerivative =
    (h0: LazyNumber) =>
        (eps: LazyNumber) =>
            (f: (a: LazyNumber) => LazyNumber) =>
                (x: LazyNumber)
                    : LazyNumber =>
                    within(eps)(superr(differentiate(h0)(f)(x)))




///////////////////////// TEST /////////////////////////

const h0 = () => 0.001;
const eps = () => 0.001;
const x = () => 1;

console.log(
    `derivative: `,
    derivative(h0)(eps)(square)(x)()
)

console.log(
    `betterDerivative: `,
    betterDerivative(h0)(eps)(square)(x)()
)
// 错了

console.log(
    `superDerivative: `,
    superDerivative(h0)(eps)(square)(x)()
)
// 错了


// 原因：TS 精度有限，导致迭代到最后反而不准了！
// 而 better derivative 和 super derivative 迭代太快了，导致过早失真。


// console.log(
//     `Derivative List: `
// )
// printList(take(()=>100, differentiate(h0)(square)(x)))

// console.log(
//     `Better Derivative List: `
// )
// printList(take(()=>20, improve(differentiate(h0)(square)(x))))

// console.log(
//     `Supper Derivative List: `
// )
// printList(take(()=>20, superr(differentiate(h0)(square)(x))))